package demo01

import (
	"fmt"
	"sync"
)

/*
6. sync.Pool
sync.Pool是在sync包下的一个内存池组件，用来实现对象的复用，避免重复创建相同的对象，造成频繁的内存分配和gc，以达到提升程序性能的目的。虽然池子中的对象可以被复用，但是是sync.Pool并不会永久保存这个对象，池子中的对象会在一定时间后被gc回收，这个时间是随机的。所以，用sync.Pool来持久化存储对象是不可取的。
另外，sync.Pool本身是并发安全的，支持多个goroutine并发的往sync.Poo存取数据

6.1 sync.pool使用方法
关于sync.Pool的使用，一般是通过三个方法来完成的

方法	说明
New()	sync.Pool的构造函数，用于指定sync.Pool中缓存的数据类型，当调用Get方法从对象池中获取对象的时候，对象池中如果没有，会调用New方法创建一个新的对象
Get()	从对象池取对象
Put()	往对象池放对象，下次Get的时候可以复用
*/

/*
在程序中，首先初始化一个sync.Pool对象，初始化里面的New方法，用于创建对象，这里是返回一个Student类型的指针。第一次调用pool.Get().(*Student)的时候，由于池子内没有对象，
所以会通过New方法创建一个，注意pool.Get()返回的是一个interface{}，所以我们需要断言成*Student类型，在我们使用完，打印出Name和Age之后，再调用Put方法，将这个对象放回到池子内，
后面我们紧接着又调用pool.Get()取对象，可以看到两次去除的对象地址是同一个，说明是同一个对象，表明sync.Pool有缓存对象的功能。
注意
我们在第一次pool.Get()取出*Student对象打印完地址之后，put进池子的时候没有进行一个Reset的过程，这里是因为我们取出*Student对象之后，仅仅是读取里面的字段，并没有修改操作，
假设我们有修改操作，那么这里就需要在pool.Put(st)之前执行Reset，将对象的值复原，如果不这样做，那么下一次pool.Get()取出的*Student对象就不是我们希望复用的初始对象
假设我们对*Student做修改
*/

func SyncPoolDemo01() {
	pool := sync.Pool{
		New: func() interface{} {
			return &Student{"屌毛", 99}
		},
	}
	st := pool.Get().(*Student)
	fmt.Println(st.Age, st.Name)
	fmt.Printf("addr is %p\n", st)

	pool.Put(st)

	st1 := pool.Get().(*Student)
	fmt.Println(st1.Age, st1.Name)
	fmt.Printf("addr1 is %p\n", st)
}

func SyncPoolDemo02() {
	pool := sync.Pool{
		New: func() interface{} {
			return &Student{"屌毛", 99}
		},
	}
	st := pool.Get().(*Student)
	fmt.Println(st.Age, st.Name)
	fmt.Printf("addr is %p\n", st)

	// 修改
	st.Name = "屌毛2"
	st.Age = 96
	// 回收
	pool.Put(st)

	st1 := pool.Get().(*Student)
	fmt.Println(st1.Age, st1.Name)
	fmt.Printf("addr1 is %p\n", st)
}

/*
可以看到，我们第二次取出的对象虽然和第一次是同一个，地址形同，但是对象的字段值却发生了变化，不是我们初始化的对象了，我们想要一只重复使用一个相同的对象的话，显然这里有问题。
所以，我们需要在pool.Put(st)回收对象之前，进行对象的Reset操作，将对象值复原，
同时在每次我们pool.Get()取出完对象使用完毕之后，也不要忘了调用pool.Put方法把对象再次放入对象池，以便对象能够复用。

6.2 sync.pool使用场景
	1.sync.pool主要是通过对象复用来降低gc带来的性能损耗，所以在高并发场景下，由于每个goroutine都可能过于频繁的创建一些大对象，造成gc压力很大。
		所以在高并发业务场景下出现gc问题时，可以使用 sync.Pool 减少 gc 负担
	2.sync.pool不适合存储带状态的对象，比如socket 连接、数据库连接等，因为里面的对象随时可能会被gc回收释放掉
	3.不适合需要控制缓存对象个数的场景，因为Pool 池里面的对象个数是随机变化的，因为池子里的对象是会被gc的，且释放时机是随机的
*/
